import { notification } from "antd"
import sourceMap, { RawSourceMap } from "source-map-js"

type sourceType = {
    fileName: string
    line: number
    column: number
}

// 找到以.js结尾的文件名称
function matchStr(str: string) {
    // 判断一个字符串是否以指定的子字符串结尾，如果是返回true，否则返回false
    if (str.endsWith(".js")) {
        // 比如 str = "http://localhost:8083/js/home.af9aa02d.js"   截取之后就是"home.af9aa02d.js"
        return str.substring(str.lastIndexOf("/") + 1)
    }
}

// 将所有的空格转化为实体字符
function replaceAll(str: string) {
    return str.replace(new RegExp(" ", "gm"), "&nbsp;")
}

// 从服务器获取Source Map文件
function loadSourceMap(fileName: string): Promise<RawSourceMap | undefined> {
    const file = matchStr(fileName)
    // 如果file名为空，就直接返回
    if (!file) return Promise.resolve(undefined)
    return new Promise(resolve => {
        fetch(`http://localhost:8118/source-map/getmap?fileName=${file}`).then(response => {
            resolve(response.json())
        })
    })
}

export async function findCodeBySourchMap({ fileName, line, column }: sourceType) {
    console.log(fileName)

    // 获取map文件
    const sourceData = await loadSourceMap(fileName)
    console.log("sourceData:", sourceData)

    // 如果文件不存在就直接返回
    if (!sourceData) return
    const { sourcesContent = [], sources = [] } = sourceData
    console.log("sourcesContent:", sourcesContent)

    // 解析和操作 Source Map 数据，将其转换为一个可以高效查询的内部表示形式，通过 consumer 来访问和操作 Source Map 的各种功能，例如查找原始代码的位置、获取映射信息等
    const consumer = await new sourceMap.SourceMapConsumer(sourceData)

    // 查找指定压缩后代码位置对应的原始代码位置
    const result = consumer.originalPositionFor({
        line: Number(line),
        column: Number(column)
    })
    console.log(result.source)

    /**
     * result结果
     * {
     *   "source": "webpack://myapp/src/views/HomeView.vue",
     *   "line": 24,  // 具体的报错行数
     *   "column": 0, // 具体的报错列数
     *   "name": null
     * }
     * */
    if (result.source && result.source.includes("node_modules")) {
        // 第三方报错解析不了，缺少第三方的map文件
        // 比如lechart报错 webpack://web-see/node_modules/.pnpm/echarts@5.4.1/node_modules/echarts/Lib/util/model.js
        notification.error({
            message: "源码解析失败",
            description: `因为报错来自三方依赖，报错文件为 ${result.source}`,
            duration: 5
        })
        return
    }

    let index = sources.indexOf(result.source)

    let copySources = sources

    // 未找到，将sources路径格式化后重新匹配 /./ 替换成 /
    // 测试中发现会有路径中带/./的情况，如 webpack://web-see/./src/main.js
    if (index === -1) {
        // 将/./ 替换成 /，\表示转义，g表示全局
        copySources = JSON.parse(JSON.stringify(sources)).map((item: string) => item.replace(/\/.\//g, "/"))
    }
    // 重新匹配
    index = copySources.indexOf(result.source)

    if (index === -1) {
        notification.error({
            message: "源码解析失败", // 通知的标题
            description: "由于某些原因，源码解析未能成功完成。", // 通知的详细内容
            duration: 5 // 消息显示的持续时间（秒），设置为 null 表示消息不会自动关闭
        })
        return
    }

    const code = sourcesContent[index]
    const codeList = code.split("\n")

    const row = result.line,
        len = codeList.length - 1
    const start = row - 5 >= 0 ? row - 5 : 0, // 将报错代码显示在中间位置
        end = start + 9 >= len ? len : start + 9 // 最多展示10行
    const newLines = []
    let j = 0

    for (let i = start; i <= end; i++) {
        j++
        newLines.push({
            start,
            j,
            row,
            replaceContent: replaceAll(codeList[i])
        })
    }

    return {
        source: result.source,
        row: row,
        column: result.column,
        newLines: newLines
    }
}
